#include <unistd.h>

#include <PSUInfo.hpp>
#include <Utils.hpp>
#include <sdbusplus/asio/connection.hpp>
#include <sdbusplus/asio/object_server.hpp>

#include <iostream>
#include <istream>
#include <limits>
#include <memory>
#include <string>
#include <variant>
#include <vector>

static constexpr const char* sensorPathPrefix =
    "/xyz/openbmc_project/sensors/power/";
static constexpr const char* sensorInterface =
    "xyz.openbmc_project.Inventory.Decorator.Asset";

//<dbus上的name, 驱动节点下的filename>
static boost::container::flat_map<std::string, std::string> propertyNamesMap = {
    {"Manufacturer", "vendor"},
    {"Model", "model"},
    {"SerialNumber", "serial_number"},
    {"PartNumber", "part_number"},
};

// using namespace phosphor::tencent;
PSUInfo::PSUInfo(sdbusplus::asio::object_server& objectServer,
                 std::shared_ptr<sdbusplus::asio::connection>& conn,
                 const std::string& sensorName, const std::string& filePath) :
    objServer(objectServer),
    conn(conn), sensorName(sensorName), filePath(filePath)
{
    //构建关于powersupply的interface 便于查找path
    powersupplyIface = objServer.add_interface(
        sensorPathPrefix + sensorName,
        "xyz.openbmc_project.Inventory.Item.PowerSupply");
    std::string val = "PowerSupply";
    powersupplyIface->register_property("Type", val);
    double_t values1 = 0;
    powersupplyIface->register_property("RatedPower", values1);
    uint8_t deviceNum = getDeviceNum(sensorName);
    powersupplyIface->register_property("DeviceNum", deviceNum);
    powersupplyIface->initialize();
    //版本信息的interface
    versionIface = objServer.add_interface(
        sensorPathPrefix + sensorName,
        "xyz.openbmc_project.Inventory.Decorator.Revision");
    val = "";
    versionIface->register_property("Version", val);
    versionIface->initialize();
    //Software Version Inventory
    fwVersionIface = objServer.add_interface(
        "/xyz/openbmc_project/software/" + sensorName,
        "xyz.openbmc_project.Software.Version");
    val = "xyz.openbmc_project.Software.Version.VersionPurpose.PSU";
    fwVersionIface->register_property("Purpose", val);
    val = "null";
    fwVersionIface->register_property("Version", val);
    fwVersionIface->initialize();

    // psu静态信息的interface
    std::string values = "";
    psuinfoInterface =
        objServer.add_interface(sensorPathPrefix + sensorName, sensorInterface);
    for (const auto& propertyName : propertyNamesMap)
    {
        psuinfoInterface->register_property(propertyName.first, values);
    }
    psuinfoInterface->register_property("BuildDate", values);
    psuinfoInterface->register_property("SubModel", values);
    psuinfoInterface->register_property("SparePartNumber", values);
    int fruId = -1;
    std::string fruName;
    if(sensorName == "PSU0")
    {
        fruId = 30;
        fruName = "PSU0_FRU";
    }
    else if(sensorName == "PSU1")
    {
        fruId = 31;
        fruName = "PSU1_FRU";
    }
    psuinfoInterface->register_property("FRU_NAME", fruName);
    psuinfoInterface->register_property("FRU_ID", fruId);
    psuinfoInterface->initialize();

    std::shared_ptr<sdbusplus::asio::dbus_interface> association =
        objectServer.add_interface(sensorPathPrefix + sensorName,
                                   association::interface);
    createInventoryAssoc(conn, association,
                         "/xyz/openbmc_project/sensors/power/" + sensorName);
    updatePSUInfo();
    snChanged();
}

//计算额定功耗
double_t PSUInfo::formartPower(int power)
{
    int y = power & 0x7ff;
    int n = (power >> 11) & 0x1f; //高5位
    int x = 0;
    double_t res = NAN;
    if (0x10 == (n & 0x10))
    {
        //符号位取反+1
        x = ((~n) & 0x1f) + 1;
        res = floor((double_t)y / (double_t)(1 << x));
    }
    else
    {
        x = n;
        res = y * (1 << x);
    }
    return res;
}

int PSUInfo::readPSUInfo(const std::string& propertyFileName,
                         std::string& value)
{
    std::string propertyPath = filePath + "/" + propertyFileName;
    std::ifstream propertyFile(propertyPath);
    static constexpr auto retryMax = 10;
    static int retry = 0;
    if (!propertyFile.good())
    {
        std::cerr << "Open" << propertyPath << "file faild.\n";
        return -1;
    }
    std::getline(propertyFile, value);
    propertyFile.close();

    //电源插拔后，驱动读到的value值为空，要保证读到正确的值
    if ((value.empty()) && (retry < retryMax))
    {
        sleep(1);
        retry++;
        readPSUInfo(propertyFileName, value);
    }
    else
    {
        retry = 0;
    }

    return 0;
}

// FIX:插拔电源模块，PSUInfo未更新
void PSUInfo::updatePSUInfo(void)
{
    int ret = 0;
    std::string val = "";

    ret = readPSUInfo("output_power_max", val);
    if (ret == 0)
    {
        //计算额定功耗
        char* endPtr = nullptr;
        double_t val1 = formartPower(strtol(val.c_str(), &endPtr, 10));
        if (*endPtr != '\0')
        {
            std::cerr << "Convert output_power_max to number faild.\n";
            powersupplyIface->set_property("RatedPower", NAN);
        }
        else
        {
            powersupplyIface->set_property("RatedPower", val1);
        }
        psuRatedPower = val1;
    }
    ret = readPSUInfo("fw_version", val);
    if (ret < 0)
    {
        std::cerr << "fw_version faild.\n";
    }
    versionIface->set_property("Version", val);
    fwVersionIface->set_property("Version", val);
    //填充inventoryData数据，用于转换成rawdata
    inventoryData["Product"].emplace("Version",val);

    for (const auto& propertyName : propertyNamesMap)
    {
        ret = readPSUInfo(propertyName.second, val);
        if (ret < 0)
        {
            continue;
        }

        psuinfoInterface->set_property(propertyName.first, val);
        
        if (propertyName.first == "Manufacturer")
        {
            psuManu = val;
            inventoryData["Product"].emplace("Manufacturer",val);
        }
        // For PSU_Mismatch
        if (propertyName.first == "Model")
        {
            psuModel = val;
            inventoryData["Product"].emplace("Name",val);
        }
        if (propertyName.first == "PartNumber")
        {
            inventoryData["Product"].emplace("Model Number",val);
        }
        // For PSU硬件变更
        if (propertyName.first == "SerialNumber")
        {
            psuSerialNum = val;
            inventoryData["Product"].emplace("Serial Number",val);
        }

    }
    fruRawData = ipmi::fru::buildFruAreaData(inventoryData);
}

void PSUInfo::snChanged()
{
    if (!LastpsuSerialNum.empty())
    {
        if (LastpsuSerialNum != psuSerialNum)
        {
            std::string msg = sensorName + " SerialNumber change from " +
                              LastpsuSerialNum + " to " + psuSerialNum;
        }
    }
    LastpsuSerialNum = psuSerialNum;
}

std::string PSUInfo::getPSUModel(void)
{
    return psuModel;
}

std::string PSUInfo::getPSUManu(void)
{
    return psuManu;
}

// 获取单个psu的额定功率
double PSUInfo::getPSURatedPower(void)
{
    return psuRatedPower;
}

void PSUInfo::psuInfoClear()
{
    std::string val = "";
    double_t val1 = 0;
    powersupplyIface->set_property("RatedPower", val1);
    versionIface->set_property("Version", val);
    fwVersionIface->set_property("Version", val);
    for (const auto& propertyName : propertyNamesMap)
    {
        psuinfoInterface->set_property(propertyName.first, val);
    }
    fruRawData.clear();
}
std::vector<uint8_t> PSUInfo::getFRUInfo()
{
    return fruRawData;
}
